import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

class SingletonDemo implements Serializable, Cloneable {

	private static final long serialVersionUID = 894353099942484686L;

	private static SingletonDemo instance = null;

	private SingletonDemo() {
		System.out.println("Inside private constructor..");
	}

	public synchronized static SingletonDemo getInstance() {
		if (instance == null) {
			synchronized (SingletonDemo.class) {
				if (instance == null) {
					instance = new SingletonDemo();
				}
			}

		}
		return instance;
	}

	@Override
	protected Object clone() throws CloneNotSupportedException {
		System.out.println("Inside clone method..");
		return instance.clone();
		// throw new CloneNotSupportedException();
	}

}

class serializeSingleton {
	SingletonDemo obj = null;

	protected void serialize() {
		try {
			obj = SingletonDemo.getInstance();
			FileOutputStream fs = new FileOutputStream("testSer.ser");
			ObjectOutputStream os = new ObjectOutputStream(fs);
			os.writeObject(obj); // 3
			os.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	protected SingletonDemo deSerialize() {
		try {
			FileInputStream fis = new FileInputStream("testSer.ser");
			ObjectInputStream ois = new ObjectInputStream(fis);
			obj = (SingletonDemo) ois.readObject(); // 4
			ois.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return obj;
	}

}

class DemoSingleton implements Cloneable {

	private int i = 10;
	Employee emp = new Employee(1, "Ramesh", 22);

	private void display() {
		System.out.println("i = " + i);
		System.out.println("Employee : " + emp);
	}

	private void setValue(int j) {
		this.i = j;
	}

	private void setEmployee(Employee e) {
		this.emp = e;
	}

	private Employee getEmployee() {
		return emp;
	}

	public static void main(String args[]) {
		SingletonDemo obj1 = null, obj2 = null;
		Employee emp = null;
		try {
			DemoSingleton demo = new DemoSingleton();
			emp = demo.getEmployee();
			demo.setValue(20);
			demo.display();
			emp.setAge(22);
			emp.setName("sk");
			emp.setRoll(2);
			demo.setEmployee(emp);
			DemoSingleton demo1 = (DemoSingleton) demo.clone();
			System.out.println("clobed hashCode : " + demo1.hashCode());
			demo1.display();
			obj1 = SingletonDemo.getInstance();
			obj2 = SingletonDemo.getInstance();

		} catch (CloneNotSupportedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		serializeSingleton serObj = new serializeSingleton();
		System.out.println("Demo1 = " + obj1.hashCode());
		System.out.println("Demo2 = " + obj2.hashCode());
		serObj.serialize();
		SingletonDemo obj3 = serObj.deSerialize();
		SingletonDemo obj4 = serObj.deSerialize();
		System.out.println("Demo3 = " + obj3.hashCode());
		System.out.println("Demo4 = " + obj4.hashCode());

	}

	class Employee {
		private int roll;
		private String Name;
		private int age;

		/**
		 * @param roll
		 * @param name
		 * @param age
		 */
		public Employee(int roll, String name, int age) {
			super();
			this.roll = roll;
			Name = name;
			this.age = age;
		}

		public Employee() {

		}

		/*
		 * (non-Javadoc)
		 * 
		 * @see java.lang.Object#toString()
		 */
		@Override
		public String toString() {
			return "Employee [" + (Name != null ? "Name=" + Name + ", " : "")
					+ "age=" + age + ", roll=" + roll + "]";
		}

		/**
		 * @return the roll
		 */
		public int getRoll() {
			return roll;
		}

		/**
		 * @param roll
		 *            the roll to set
		 */
		public void setRoll(int roll) {
			this.roll = roll;
		}

		/**
		 * @return the name
		 */
		public String getName() {
			return Name;
		}

		/**
		 * @param name
		 *            the name to set
		 */
		public void setName(String name) {
			Name = name;
		}

		/**
		 * @return the age
		 */
		public int getAge() {
			return age;
		}

		/**
		 * @param age
		 *            the age to set
		 */
		public void setAge(int age) {
			this.age = age;
		}

	}
}
